from problem import *
from osm_utils4 import *
from all import *
from time import time
from weightedAstar import *
import sys

class Move(ProblemAction):
	def __init__(self, start, target, cost):
		self.start = start;
		self.target = target;
		self.cost = cost;
		
	def __cmp__(self, other):
		if self.start != other.start:
			return self.start.__cmp__(other.start);
		return self.target.__cmp__(other.target);
	
	def __hash__(self):
		return hash(self.start + self.target);
	
	def __str__(self):
		return str(self.start) + "->" + str(self.target);

class Location(ProblemState):
	
	def __init__(self, identity, goal, costCalculator, countryMap, parent):
		self.identity = identity;
		self.goal = goal;
		self.costCalculator = costCalculator;
		self.countryMap = countryMap;
		self.parent = parent;
		
	def getSuccessors(self):
		successors = dict();
		myJunction = self.countryMap.GetJunction(self.identity);
		for l in myJunction.links:
			target = l.target;
			if target == self.parent:
				continue;
			cost = self.costCalculator.calculate(l);
			successors[Move(self.identity, target, cost)] = Location(target, self.goal, self.costCalculator, self.countryMap, self.identity);
		return successors;
	
	def isGoal(self):
		return (self.identity == self.goal);
	
	def __cmp__(self, other):
		return self.identity.__cmp__(other.identity);
	
	def __hash__(self):
		return hash(self.identity);
	
	def __str__(self):
		return "Location: " + str(self.identity);

#######################################################
# COST FUNCTIONS
#######################################################
class CostCalculator:
	def __init__(self, weightDistance, weightTime, weightPetrol, countryMap):
		self.weightDistance = weightDistance;
		self.weightTime = weightTime;
		self.weightPetrol = weightPetrol;
		self.countryMap = countryMap;
				
	def calculate(self, l):
		cost = 0;
		cost += self.weightDistance * l.distance;
		cost += self.weightTime *(float(l.distance) / float(l.speed));
		cost += self.weightPetrol * float(l.distance) / float(self.countryMap.PetrolConsumption(l.speed));
		return cost;

def distanceCostCalculator(countryMap):
	return CostCalculator(1,0,0, countryMap);

def timeCostCalculator(countryMap):
	return CostCalculator(0,1,0, countryMap);

def petrolCostCalculator(countryMap):
	return CostCalculator(0,0,1, countryMap);

#######################################################
# HEURISTICS
#######################################################

class weightedHeuristic:
	def __init__(self,  weightDistance, weightTime, weightPetrol, countryMap):
		self.weightDistance = weightDistance;
		self.weightTime = weightTime;
		self.weightPetrol = weightPetrol;
		self.countryMap = countryMap;
		speeds = [l.speed for j in self.countryMap.junctions for l in j.links ];
		self.maxSpeed = float(max(speeds));
		petrols = [self.countryMap.PetrolConsumption(speed) for speed in speeds];		
		self.maxPetrol = max(petrols)
	def evaluate(self, state):
		j1 = self.countryMap.GetJunction(state.identity);
		j2 = self.countryMap.GetJunction(state.goal);
		distance = float(self.countryMap.ComputeDistance(j1.lat, j1.lon, j2.lat, j2.lon));
		cost = 0;
		cost += self.weightDistance * distance;
		cost += self.weightTime *(distance / self.maxSpeed);
		cost += self.weightPetrol * distance / float(self.maxPetrol);
		return cost;
		
		
def distanceHeuristic(countryMap):
	return weightedHeuristic(1,0,0,countryMap);

def timeHeuristic(countryMap):
	return weightedHeuristic(0,1,0,countryMap);

def petrolHeuristic(countryMap):
	return weightedHeuristic(0,0,1,countryMap);



def run_problem(num, num2, start, goal, cost, m, h, s):
	startTime = time();
	a = s.find(Location(start, goal, cost, m, -1), h);
	totalTime = time() - startTime;
	
	if a == None:
		print num, num2, "start = ", start, "goal = ", goal, "not connected"
		return;
	
	j1 = m.GetJunction(start);
	j2 = m.GetJunction(goal);
	totalCost = sum([x.cost for x in a]);
	print num, num2, totalTime, m.ComputeDistance(j1.lat, j1.lon, j2.lat, j2.lon), len(a), totalCost;
	
def run_WeightedProblem(num, num2, start, goal, cost, m, h, s, w):
	startTime = time();
	a = s.find(Location(start, goal, cost, m, -1), h, w);
	totalTime = time() - startTime;
	
	if a == None:
		print num, num2, "start = ", start, "goal = ", goal, "not connected"
		return;
	
	j1 = m.GetJunction(start);
	j2 = m.GetJunction(goal);
	totalCost = sum([x.cost for x in a]);
	print num, num2, totalTime, m.ComputeDistance(j1.lat, j1.lon, j2.lat, j2.lon), len(a), totalCost;
	

if __name__ == "__main__":	
	f = open("problems.txt");
	problems = [];
	for l in f:
		l = l.split();
		start = int(l[0]);	
		goal = int(l[1]);
		problems.append((start, goal));
	
	s = AStar();

	m = CountryMap();
	print "Loading map ..."	
	m.LoadMap2();
	print "Map loaded."
	
	
	numOfRun = int(sys.argv[1]);
	startingProb = 0;
	endingProb = 100;
	if len(sys.argv) >= 3:
		startingProb = int(sys.argv[2]);
	if len(sys.argv) >= 4:
		endingProb = int(sys.argv[3]);
	
	m.car = "Toyota Rav4";
	
	if numOfRun == 1:
		costCalc = distanceCostCalculator(m);
		heuristicCalc = distanceHeuristic(m);
	elif numOfRun == 2:
		costCalc = timeCostCalculator(m);
		heuristicCalc = timeHeuristic(m);
	elif numOfRun == 3:
		m.car = "Toyota Rav4";
		costCalc = petrolCostCalculator(m);
		heuristicCalc = petrolHeuristic(m); 
	elif numOfRun == 4:
		m.car = "Ford Focus";
		costCalc = petrolCostCalculator(m);
		heuristicCalc = petrolHeuristic(m); 
	elif numOfRun == 5:
		m.car = "Toyota Rav4";
		costCalc = CostCalculator(0.4, 0.3, 0.3, m);
		heuristicCalc = weightedHeuristic(0.4, 0.3, 0.3, m);
	elif numOfRun == 6:
		m.car = "Ford Focus";
		costCalc = CostCalculator(0.4, 0.3, 0.3, m);
		heuristicCalc = weightedHeuristic(0.4, 0.3, 0.3, m);
	elif numOfRun == 7:
		m.car = "Toyota Rav4";
		s = WeightedAStar();		
		
		for w in [0.9, 0.8, 0.7, 0.6]:
			costCalc = distanceCostCalculator(m);
			heuristicCalc = distanceHeuristic(m);
			for p in range(10):
				start = problems[p][0];
				goal = problems[p][1];
				run_WeightedProblem(p, w*1000+1, start, goal, costCalc, m, heuristicCalc, s, w);
				
			costCalc = timeCostCalculator(m);
			heuristicCalc = timeHeuristic(m);
			for p in range(10):
				start = problems[p][0];
				goal = problems[p][1];
				run_WeightedProblem(p, w*1000+2, start, goal, costCalc, m, heuristicCalc, s, w);
				
			costCalc = petrolCostCalculator(m);
			heuristicCalc = petrolHeuristic(m); 
			for p in range(10):
				start = problems[p][0];
				goal = problems[p][1];
				run_WeightedProblem(p, w*1000+3, start, goal, costCalc, m, heuristicCalc, s, w);
			
			costCalc = CostCalculator(0.4, 0.3, 0.3, m);
			heuristicCalc = CostCalculator(0.4, 0.3, 0.3, m);
			for p in range(10):
				start = problems[p][0];
				goal = problems[p][1];
				run_WeightedProblem(p, w*1000+4, start, goal, costCalc, m, heuristicCalc, s, w);
			
		
		
		
		
		
	if numOfRun != 7:	
		print "NOW START TO RUN PROBLEMS"
		for p in range(startingProb, endingProb):
			start = problems[p][0];
			goal = problems[p][1];
			run_problem(p, numOfRun, start, goal, costCalc, m, heuristicCalc, s);
			